package com.alex.taskmanager.threadpool;

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ThreadPool {

	private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPool.class);

	private int threadCount = 4;

	private int maxTasks = 10;

	private boolean running = false;

	private AtomicInteger runningTaskCount;

	private WorkerThread[] taskExecutorThreads;

	private BlockingDeque<Runnable> tasks;

	public int getThreadCount() {
		return threadCount;
	}

	public void setThreadCount(int threadCount) {
		this.threadCount = threadCount;
	}

	public int getMaxTasks() {
		return maxTasks;
	}

	public void setMaxTasks(int maxTasks) {
		this.maxTasks = maxTasks;
	}

	public int getRemainingTaskCapacity() {
		return tasks.remainingCapacity();
	}

	public void start() {
		if (running) {
			throw new IllegalStateException("Thread pool is already running");
		}
		runningTaskCount = new AtomicInteger(0);
		tasks = new LinkedBlockingDeque<Runnable>(maxTasks);
		taskExecutorThreads = new WorkerThread[threadCount];
		for (int i = 0; i < threadCount; i++) {
			taskExecutorThreads[i] = new WorkerThread(new TaskExecutorWorker(), "TaskManagerExecutorThread-" + (i + 1));
			taskExecutorThreads[i].start();
		}
		running = true;
	}

	public void stop() throws InterruptedException {
		if (!running) {
			throw new IllegalStateException("Thread pool is not running");
		}
		for (WorkerThread thread : taskExecutorThreads) {
			thread.stopThread();
			thread.interrupt();
		}
		for (WorkerThread thread : taskExecutorThreads) {
			thread.join();
		}
	}

	public void addTask(Runnable runnable) throws InterruptedException {
		tasks.put(runnable);
	}

	public int getCurrentTaskCount() {
		return tasks.size() + runningTaskCount.intValue();
	}

	class TaskExecutorWorker extends WorkerWithLogging {

		public void run() throws Exception {
			Runnable task = tasks.takeFirst();
			runningTaskCount.incrementAndGet();
			try {
				task.run();
			} finally {
				runningTaskCount.decrementAndGet();
			}
		}

		@Override
		public Logger getLogger() {
			return LOGGER;
		}
	}
}
